package com.zc.gateway.security.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zc.commons.http.entity.Response;
import com.zc.gateway.entity.LoginEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 登录请求认证过滤器
 */
@Component
public class LoginRequestFilter implements WebFilter {

    private static final String METHOD_POST = "POST";
    @Value("${com.zc.gateway.login.path:/login}")
    private String loginPath;
    @Autowired
    private PathMatcher pathMatcher;

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        ServerHttpRequest request = serverWebExchange.getRequest();
        String path = request.getURI().getPath();
        if (!pathMatcher.match(loginPath, path)) {
            return Mono.empty();
        }
        if (!METHOD_POST.equals(request.getMethodValue().toUpperCase())) {
            return monoWithMethodError(serverWebExchange.getResponse());
        }
        ServerHttpRequest.Builder reqBuilder = request.mutate();


        // 获取请求参数
        Flux<DataBuffer> dataBufferFlux = request.getBody();
        LoginEntity loginEntity = getLoginEntity(dataBufferFlux);
        if (!checkLoginEntity(loginEntity)) {
            return monoWithError(serverWebExchange.getResponse(), "请求参数不正确！");
        }
        // 用户信息放入过滤器链 Authentication
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginEntity, null);
        serverWebExchange = serverWebExchange.mutate().principal(Mono.just(authenticationToken)).build();
        return webFilterChain.filter(serverWebExchange);
    }

    /**
     * 获取登录实体
     *
     * @param dataBufferFlux
     * @return
     */
    private LoginEntity getLoginEntity(Flux<DataBuffer> dataBufferFlux) {
        AtomicReference<String> strRef = new AtomicReference<>();
        dataBufferFlux.subscribe(dataBuffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
            DataBufferUtils.release(dataBuffer);
            strRef.set(charBuffer.toString());
        });
        return JSON.parseObject(strRef.get(), LoginEntity.class);
    }

    /**
     * 请求方法错误快速返回
     *
     * @param response
     * @return
     */
    private Mono<Void> monoWithMethodError(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Response<String> respData = Response.error("仅支持POST方法！");
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(respData, SerializerFeature.WriteMapNullValue).getBytes());
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 错误请求快速返回
     *
     * @param response
     * @param message
     * @return
     */
    private Mono<Void> monoWithError(ServerHttpResponse response, String message) {
        response.setStatusCode(HttpStatus.BAD_REQUEST);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Response<String> respData = Response.error(message);
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(respData, SerializerFeature.WriteMapNullValue).getBytes());
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 检查登录实体类
     *
     * @param loginEntity
     * @return
     */
    private boolean checkLoginEntity(LoginEntity loginEntity) {
        return loginEntity != null
                && !StringUtils.isEmpty(loginEntity.getUserAccount())
                && !StringUtils.isEmpty(loginEntity.getPassword());
    }


}
